/*******************************************************************************
 File Name        : freyabasedata.h
 Summary          : Base class of freya data
 Create           : 2020-09-12
 Author           : Schindleren
 Update history   :
 NO.     Author        Date         Content
 001     Schindleren   2020-09-12   Create New
 002     Schindleren   2023-08-24   add pArguments, add m_lockProperty, remove template of Unserialize
*******************************************************************************/
#ifndef FREYABASEDATA_H
#define FREYABASEDATA_H

#include "freyalib_global.h"
#include "freyaabstractstruct.h"

#include <QUuid>
#include <QDebug>
#include <QVariant>
#include <QVariantMap>
#include <QDataStream>
#include <QJsonDocument>
#include <QSharedPointer>
#include <QRunnable>
#include <QMutex>
#include <QReadWriteLock>

class FreyaBaseData;

typedef QSharedPointer<FreyaBaseData> FreyaData;

class FREYALIBSHARED_EXPORT FreyaBaseData
{
    // data and arguments
public:
    QString                 dataID;
    quint64                 command;

private:
    // ARGS means Object of FreyaAbstractStruct and derived class
    // ARGS
    FreyaAbstractStruct     *pArguments;
    // ARGS and other propertys
    QVariantMap             property;

    // Lock for "property" read and write
    QReadWriteLock          m_lockProperty;

public:
    FreyaBaseData();
    FreyaBaseData(const FreyaBaseData & other);
    ~FreyaBaseData();

    // destory ARGS
    bool DestoryArguments();

    FreyaAbstractStruct *GetArguments() const;

    // get/set Property(user property)
    QVariant GetProperty();
    void SetProperty(const QVariant &value);

    // get/set Property(custom property)
    QVariant GetProperty(const QString &key, const QVariant &defaultValue = QVariant());
    void SetProperty(const QString &key, const QVariant &value);

    // local data cannot trans over extension
    bool IsLocalData();
    void SetLocalData(bool bLocal);

    // msecs to the data reach contral
    qint64 DataDelayTime();

    QByteArray Serialize();
    bool Unserialize(const QByteArray & ba);

    // template create ARGS (contains dynamic_cast)
    template<class T>
    T *CreateArguments()
    {
        if(pArguments)
        {
            qWarning() << "FreyaLib > " << "Arguments is already exist! Its will destory and recreate!";
            delete pArguments;
        }
        T *t = new T();

        pArguments = dynamic_cast<FreyaAbstractStruct*>(t);
        if(!pArguments)
        {
            qWarning() << "FreyaLib > " << "CreateArguments type T is not a FreyaAbstractStruct!";
            delete t;
            t = nullptr;
        }
        return t;
    }

    template<class T>
    bool DestoryArguments()
    {
        T *t = NULL;
        if(pArguments)
        {
            t = dynamic_cast<T*>(pArguments);
            if(t)
            {
                delete t;
                t = NULL;
                pArguments = NULL;
            }
            else
            {
                return false;
            }
        }
        return true;
    }

    // template of get ARGS (contains dynamic_cast)
    template<class T>
    T *GetArguments()
    {
        m_lockProperty.lockForRead();
        T *t = nullptr;
        if(pArguments)
        {
            t = dynamic_cast<T*>(pArguments);
        }
        else if(property.contains(FREYALIB_FLG_ARGS))
        {
            t = new T();
            pArguments = dynamic_cast<FreyaAbstractStruct*>(t);
            if(!pArguments || !pArguments->FromVariant(property.value(FREYALIB_FLG_ARGS)))
            {
                delete t;
                t = nullptr;
            }
        }
        m_lockProperty.unlock();
        return t;
    }

    template<class T>
    void SetProperty(QVariantMap &property)
    {
        m_lockProperty.lockForWrite();
        if(property.contains(FREYALIB_FLG_ARGS))
        {
            T *t = new T();
            pArguments = dynamic_cast<FreyaAbstractStruct*>(t);
            if(!pArguments || !pArguments->FromVariant(property.value(FREYALIB_FLG_ARGS)))
            {
                delete t;
                t = nullptr;
                qWarning() << "FreyaLib > " << "SetProperty type T is not a FreyaAbstractStruct!";
            }
        }
        m_lockProperty.unlock();
    }

    static FreyaData CreateData(const quint64 & cmd = 0);
    static FreyaData CreateData(const FreyaBaseData & other);
};

class SerializeTask : public QObject, public QRunnable
{
    Q_OBJECT
public:
    SerializeTask(QByteArray *pBA, FreyaData pData, bool bIsSerialize);
    void run();

signals:
    void toTaskFinished(QByteArray *pBa, FreyaData pData, bool pBRet);

private:
    QByteArray      *m_pBA;
    FreyaData       m_pData;
    bool            m_bIsSerialize;
};

#endif // FREYABASEDATA_H
